<?php

Raise::load('core.RaiseObject');
Raise::load('core.reflection.RaiseVariable');
Raise::load('core.io.file.RaiseFile');
Raise::load('core.collection.RaiseCollection');

/**
 * Check whether Zlib is enabled or not
 */
if (function_exists('gzopen')) {

    /**
     * RaiseZip class
     * Provides Zip compression support using the Zlib extension
     *
     * @author Sam-Mauris Yong / hellclanner at live dot com
     * @license http://www.opensource.org/licenses/bsd-license New BSD License
     * @package phpraise.core.archive
     * @since 1.2
     */
    class RaiseZip extends RaiseObject {

        /**
         * The internal ZipArchive we'll be using to work on
         * @var ZipArchive
         */
        private $zipObj;
        /**
         * The RaiseFile representation of the ZipArchive
         * @var RaiseFile
         */
        private $file;

        /**
         * Create a new RaiseZip file
         * If the file already exists, the Zip file will be opened
         * @param RaiseFile|string $file
         */
        public function __construct($file) {
            if(!class_exists('ZipArchive')){
            throw new RaiseInvalidRequestException('Unable to create RaiseZip because the php_zip extension is not installed or enabled in PHP.');
            }
            if (RaiseVariable::isType($file, 'string')) {
                $file = new RaiseFile($file);
            }
            if (!($file instanceof RaiseFile)) {
                throw new RaiseInvalidArgumentException('RaiseZip expecting RaiseFile as first parameter; ' . RaiseVariable::typeInfo($file) . ' was given instead.');
            }
            $this->file = $file;
            $this->zipObj = new ZipArchive();
            $r = $this->zipObj->open($this->file, ZipArchive::CREATE);
            if (!$r) {
                throw new RaiseZipException('Failed to open zip file \'' . $this->file->pathname() . '\'. Internal message: ' . $this->zipObj->getStatusString());
            }
        }

        /**
         * Get the RaiseFile object representing this RaiseZip
         * @return RaiseFile
         */
        public function file() {
            return $this->file;
        }

        /**
         * Get or set the comment of the ZIP archive
         * @param string $s (optional) Set the comment of the zip archive to this value
         */
        public function comment($s = null) {
            if (func_num_args() == 1) {
                $this->zipObj->setArchiveComment($s);
            }
            return $this->zipObj->getArchiveComment();
        }

        /**
         * Fetch the entire list of entries in the Zip archive
         * @return RaiseCollection
         */
        public function entries() {
            $list = new RaiseCollection();
            for ($u = 0; $u < $this->zipObj->numFiles; $u++) {
                $list[] = $this->zipObj->getNameIndex($u);
            }
            return $list;
        }

        /**
         * Add a file from the file system to the archive
         * @param RaiseFile $file The file to add
         * @param string $dest (optional) The destination of the file added to the archive
         */
        public function addFile($file, $dest = null) {
            if (!($file instanceof RaiseFile)) {
                throw new RaiseInvalidArgumentException('RaiseZip::addFile() expecting RaiseFile as first parameter; ' . RaiseVariable::typeInfo(file) . ' was given instead.');
            }
            if ($file->exists()) {
                $this->zipObj->addFile($file->pathname(), $dest);
            } else {
                throw new RaiseZipException('Failed to add file \'' . $file->pathname() . '\' when the file does not exist.');
            }
        }

        /**
         * Delete a particular file from the archive
         * @param string $file
         */
        public function deleteFile($file) {
            $r = $this->zipObj->deleteName($file);
            if (!$r) {
                throw new RaiseZipException('Failed to delete file \'' . $file . '\' from zip archive \'' . $this->file->pathname() . '\' Internal message: ' . $this->zipObj->getStatusString());
            }
        }

        /**
         * Delete files from the achive whose name matches the regular expression provided
         * @param RaiseRegex $regex
         */
        public function deleteRegex($regex) {
            for ($u = 0; $u < $this->zipObj->numFiles; $u++) {
                $name = $this->zipObj->getNameIndex($u);
                if ($regex->match($name)->count() > 0) {
                    $this->deleteFile($name);
                }
            }
        }

        /**
         * Extract all files to a particular directory, maintaining the folder structure.
         * @param string $dest
         */
        public function extract($dest) {
            $r = $this->zipObj->extractTo($dest);
            if (!$r) {
                throw new RaiseZipException('Failed to extract all files from zip archive \'' . $this->file->pathname() . '\' to \'' . $dest . '\'. Internal message: ' . $this->zipObj->getStatusString());
            }
        }

        /**
         * Extract a particular file
         * @param string $file Pathname to the file inside the zip file to extract
         * @param RaiseFile $destFile
         */
        public function extractFile($file, $destFile) {
            if (!($destFile instanceof RaiseFile)) {
                throw new RaiseInvalidArgumentException('RaiseZip::extractFile() expecting RaiseFile as second parameter; ' . RaiseVariable::typeInfo($destFile) . ' was given instead.');
            }
            $r = $this->zipObj->extractTo($destFile->pathname(), $file);
            if (!$r) {
                throw new RaiseZipException('Failed to extract file \'' . $file . '\' from zip archive \'' . $this->file->pathname() . '\' to \'' . $destFile->pathname() . '\'. Internal message: ' . $this->zipObj->getStatusString());
            }
        }

        public function dispose() {
            $this->zipObj->close();
        }

    }

}